package sicnu.cs.ich.user.service.impl;

import sicnu.cs.ich.api.domain.entity.Validate;
import sicnu.cs.ich.user.mapper.ValidateMapper;
import sicnu.cs.ich.user.service.IValidateService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import sicnu.cs.ich.common.services.ISendMailService;
import sicnu.cs.ich.security.config.AppProperties;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * @author 沈洋 邮箱:1845973183@qq.com
 * @create 2021/8/16-10:32
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class ValidateServiceImpl implements IValidateService {

    private final ValidateMapper validateMapper;
    private final ISendMailService sendMailService;
    private final AppProperties appProperties;
    @Value("${spring.mail.username}")
    private String sender;
//    private JavaMailSender javaMailSender = new JavaMailSenderImpl();

    /**
     * 发送邮件：@Async进行异步调用发送邮件接口
     */
    @Override
    @Async
    public void sendPasswordResetEmail(HttpServletRequest request, String email, String token) {
        // 设置邮件内容
        String appUrl = request.getScheme() + "://" + appProperties.getClient().getIp();
        SimpleMailMessage mail = new SimpleMailMessage();
        mail.setFrom(sender);
        mail.setTo(email);
        mail.setSubject("【非遗传承保护平台】邮箱验证");
        mail.setText("您正在申请重置密码，请点击此链接重置密码: \n" + appUrl + "/resetPassword?token=" + token);
        sendMailService.sendEmail(mail);
    }

    /**
     * 在pm_validate表中插入一条validate记录，userid，email属性来自pm_user表，token由UUID生成
     */
    @Override
    public int insertNewResetRecord(Validate validate) {
        return validateMapper.insert(validate);
    }


    /**
     * pm_validate表中，通过token查找重置申请记录
     */
    @Override
    public Validate findUserByResetToken(String token) {
        return validateMapper.selectByToken(token);
    }

    /**
     * 验证是否发送重置邮件：每个email的重置密码每日请求上限为requestPerDay次，与上一次的请求时间间隔为interval分钟。
     *
     * @param email         邮箱
     * @param requestPerDay 每日请求上线
     * @param interval      间隔时间分钟
     * @return 是否发送
     */
    @Override
    public boolean sendValidateLimitation(String email, long requestPerDay, long interval) {
        List<Validate> validates = validateMapper.selectByEmail(email);
        // 若查无记录，意味着第一次申请，直接放行
        if (validates.isEmpty()) {
            return true;
        }
        // 有记录，则判定是否频繁申请以及是否达到日均请求上线
        long countTodayValidation = validates.stream().filter(x -> DateUtils.isSameDay(x.getModifyDate(), new Date())).count();
        long intervalForLastRequest = getIntervalForLastRequest(validates);
        return countTodayValidation <= requestPerDay && intervalForLastRequest >= interval * 60 * 1000;
    }

    public long getIntervalForLastRequest(List<Validate> validates) {
        Optional<Date> validate = validates.stream().map(Validate::getModifyDate).max(Date::compareTo);
        Date dateOfLastRequest = new Date();
        if (validate.isPresent()) {
            dateOfLastRequest = validate.get();
        }
        return System.currentTimeMillis() - dateOfLastRequest.getTime();
    }

    /**
     * 验证连接是否失效：链接有两种情况失效 1.超时 2.最近请求的一次链接自动覆盖之前的链接（待看代码）
     *
     * @param email         邮箱
     * @param requestPerDay 每日申请次数
     * @param interval      间隔时间
     * @return 是否失效
     */
    @Override
    public boolean validateLimitation(String email, long requestPerDay, long interval, String token) {
        List<Validate> validates = validateMapper.selectByEmail(email);
        // 有记录才会调用该函数，只需判断是否超时
        Optional<Date> validate = validates.stream().map(Validate::getModifyDate).max(Date::compareTo);
        Date dateOfLastRequest = new Date();
        if (validate.isPresent()) {
            dateOfLastRequest = validate.get();
        }
        long intervalForLastRequest = System.currentTimeMillis() - dateOfLastRequest.getTime();
        Optional<Date> lastRequestToken = validates.stream().filter(x -> x.getResetToken().equals(token)).map(Validate::getModifyDate).findAny();
        Date dateOfLastRequestToken = new Date();
        if (lastRequestToken.isPresent()) {
            dateOfLastRequestToken = lastRequestToken.get();
        }
        return intervalForLastRequest <= interval * 60 * 1000 && dateOfLastRequest == dateOfLastRequestToken;
    }
}
